# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         testsuite_task.py
# Description:  批量执行测试场景
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2019/11/28 13:37
# -------------------------------------------------------------------------------
import threading
from celery import shared_task

from apps.BasicAuthService.models import User
from apps.CeleryScheduledTaskService.HttpAutoTestService.utils import send_testreport
from apps.HttpAutoTestService.core.http_client import HttpSession
from apps.HttpAutoTestService.core.http_session_context import HttpSessionContext
from apps.HttpAutoTestService.core.http_testsuite_runner import HttpTestsuiteRunner
from apps.HttpAutoTestService.core.loader import load_ext_method_online
from apps.HttpAutoTestService.models import Testsuite, TestcaseResult, ExtMethodResult
from utils.common import get_version


def exec_testsuite(testsuites, executor, is_periodictask, version, http_session=None, http_session_context=None,
                   is_testsuite_set=False, ext_method_online_module=None, ext_methods_online=None):
    """
    执行测试场景
    当environment、project、http_session、http_session_context不为None时，表示正在运行测试场景集中的场景；否则表示正在运行多个单场景
    Args:
        testsuites: 被执行的场景集合
        executor: 当前执行(登录)用户
        is_periodictask: 标记是否是定时任务
        version: 版本号
        http_session: HttpSession实例（执行场景集时使用）
        http_session_context: HttpSessionContext实例（执行场景集时使用）
        is_testsuite_set: 是否执行的是测试场景集
        ext_method_online_module: ext_method_online.py模块
        ext_methods_online: ext_method_online.py模块中的方法

    Returns:

    """
    # 动态加载ext_method_online.py模块中内容
    if ext_method_online_module is None:
        ext_method_online_module, ext_methods_online = load_ext_method_online()
    for testsuite in testsuites:
        # 1、获取场景所属项目
        project = testsuite.project
        # 2、获取场景私有环境或项目默认运行环境，场景私有环境优先于项目默认运行环境
        if testsuite.enable_private_env:
            # 获取场景私有环境
            environment = testsuite.private_environment
        else:
            # 获取项目默认运行环境
            environment = project.default_environment
        # 3、获取环境全局参数
        environment_variables = environment.global_variables
        # 4、获取项目的全局参数
        project_variables = project.global_variables
        # 5、获取场景的全局参数
        testsuite_variables = testsuite.global_variables
        if is_testsuite_set is False:
            # 批量运行测试场景
            # 6、创建HttpSession实例
            http_session = HttpSession()
            # 7、创建HttpSessionContext实例
            http_session_context = HttpSessionContext(
                environment_variables=environment_variables,
                project_variables=project_variables,
                testsuite_variables=testsuite_variables)
        else:
            # 运行测试场景集
            http_session_context.update_session_variables(environment_variables)
            http_session_context.update_session_variables(project_variables)
            http_session_context.update_session_variables(testsuite_variables)
        # 8、创建HttpTestsuiteRunner实例
        http_testsuite_runner = HttpTestsuiteRunner(http_session=http_session,
                                                    http_session_context=http_session_context,
                                                    environment=environment,
                                                    project=project,
                                                    testsuite=testsuite,
                                                    ext_method_online_module=ext_method_online_module,
                                                    ext_methods_online=ext_methods_online)
        testsuite_result, testcase_result_list, ext_method_result_list = http_testsuite_runner.run(
            executor=executor,
            is_periodictask=is_periodictask)
        testsuite_result.version = version
        for testcase_result in testcase_result_list:
            testcase_result.testsuite_result = testsuite_result
            testcase_result.version = version

        for ext_method_result in ext_method_result_list:
            ext_method_result.testsuite_result = testsuite_result
            ext_method_result.version = version

        # 1、保存场景结果
        testsuite_result.save()
        # 2、更新场景状态
        testsuite.status = testsuite_result.status
        testsuite.save()
        # 3、保存用例结果、扩展方法运行结果
        TestcaseResult.objects.bulk_create(testcase_result_list)
        ExtMethodResult.objects.bulk_create(ext_method_result_list)


@shared_task(name="批量执行测试场景:HttpAutoTestService.testsuite_task.batch_exec_testsuite")
def batch_exec_testsuite(testsuites=None, executor_id=None, is_periodictask=True, version=None):
    """
    批量执行测试场景
    Args:
        testsuites: 当前执行(登录)用户id
        executor_id: 被执行的场景id集合
        is_periodictask: 标记是否是定时任务
        version: 版本号

    Returns:

    """
    # 获取执行者
    executor = User.objects.filter(id=executor_id, is_deleted=False).first()

    if version is None:
        # 执行定时任务时，参数version为None，此需要设置version；当批量执行时，参数version为传入值
        version = get_version()
    # 执行定时任务时，参数testsuites类型为字符串，需要转换成列表；当批量执行时，参数testsuites类型为列表
    if isinstance(testsuites, str):
        testsuites = eval(testsuites) if isinstance(eval(testsuites), list) else []
    testsuite_list = Testsuite.objects.filter(id__in=testsuites, is_deleted=False)

    # 启动线程数量
    threads_num = 5
    quotient, remainder = divmod(len(testsuite_list), threads_num)

    testsuites_temp = [[] for i in range(threads_num)]
    for i in range(threads_num):
        testsuites_temp[i] = testsuite_list[i * quotient:(i + 1) * quotient]

    for i in range(remainder):
        testsuites_temp[i].append(testsuite_list[threads_num * quotient + i])

    threads = []
    for i in range(threads_num):
        t = threading.Thread(target=exec_testsuite, args=(testsuites_temp[i], executor, is_periodictask, version))
        threads.append(t)

    for t in threads:
        t.start()

    for t in threads:
        t.join()

    # 此处可以增加发送测试报告
    send_testreport(report_type='testsuite', version=version, executor=executor, is_periodictask=is_periodictask)

    return f"批量执行场景已完成【用户ID：{executor_id}，场景ID：{testsuites}，是否是定时任务：{is_periodictask}，版本号：{version}】"
